/*Type in the calculator example and get it to work. Do not save time
  by using an alreadyentered text. You'll learn most from finding and correcting
  little silly errors.*/
//File: ch6/6.18.cpp
#include <iostream>
#include <string>
#include <map>
#include <cctype>
#include <sstream>
using namespace std;
istream* input; //pointer to input stream
namespace Error
{
    int no_of_errors;
    double error(const string& s)
    {
        no_of_errors++;
        cerr << "error:" << s << endl;
        return 1;
    }
}
namespace Lexer{
    map<string,double> table;
    enum Token_value
    {
        NAME,   NUMBER,   END,
        PLUS='+', MINUS='-',MUL='*',DIV='/',
        PRINT=';',ASSIGN='=',LP='(',RP=')'
    };
    Token_value curr_tok = PRINT;
    double number_value;
    string string_value;

    /*get a token from the input stream, and the global var
      cur_tok indicates the token's type*/
    Token_value get_token()
    {
        char ch = 0;
        do{//skip the space
            if(!input->get(ch)) return curr_tok = END;
        }while(ch != '\n' && isspace(ch));
    
        switch(ch){
        case ';':
        case '\n':
            return curr_tok = PRINT;//return and ...
        case '*':
        case '/':
        case '+':
        case '-':
        case '(':
        case ')':
        case '=':
            return curr_tok = Token_value(ch);
        case '0':case'1':case '2': case '3':case '4':
        case '5':case '6':case '7':case '8':case '9':
        case '.':
            input->putback(ch);
            (*input)>> number_value;
            return curr_tok = NUMBER;
        default:
            if(isalpha(ch)){
                input->putback(ch);
                (*input) >> string_value;
                return curr_tok = NAME;
            }
            Error::error("bad token");
            return curr_tok = PRINT;
        }
    }
}
namespace Parser
{
    double prim(bool get);
    double term(bool get);
    


    
    double expr(bool get)//plus and minus
    {
        double left = term(get);
        for(;;)
            switch(Lexer::curr_tok)
            {
            case Lexer::PLUS:
                left += term(true);
                break;
            case Lexer::MINUS:
                left -= term(true);
                break;
            default:
                return left;
            }
    }
    double term(bool get)
    {
        double left = prim(get);
        for(;;)
            switch(Lexer::curr_tok){
            case Lexer::MUL:
                left *= prim(true);
                break;
            case Lexer::DIV:
                if(double d = prim(true)){
                    left /= d;
                    break;
                }
                return Error::error("divide by 0");
            default:
                return left;
            }
    }
    double prim(bool get)
    {
        if(get) Lexer::get_token();
        switch(Lexer::curr_tok){
        case Lexer::NUMBER:
        {
            double v = Lexer::number_value;
            Lexer::get_token();
            return v;
        }
        case Lexer::NAME:
        {
            double& v = Lexer::table[Lexer::string_value];
            if(Lexer::get_token() == Lexer::ASSIGN) v = expr(true);
            return v;
        }
        case Lexer::MINUS:
            return -prim(true);
        case Lexer::LP:
        {
            double e = expr(true);
            if(Lexer::curr_tok != Lexer::RP)return Error::error(") expected");
            Lexer::get_token();//eat ')'
            return e;
        }
        default:
            return Error::error("primary expected");
        }
    }
}


int main(int argc, char* argv[])
{
    switch(argc){
    case 1:
        input = &cin;
        break;
    case 2:
        input = new istringstream(argv[1]);
        break;
    default:
        Error::error("You give me too many arguments!");
        return 1;
    }
    Lexer::table["pi"] = 3.1415926535897932385;
    Lexer::table["e"]= 2.7182818284590452354;
    while(*input){
        Lexer::get_token();
        if(Lexer::curr_tok == Lexer::END)break;
        if(Lexer::curr_tok == Lexer::PRINT)continue;
        cout << Parser::expr(false) << endl;
    }
    if(input != &cin) delete input;
    return Error::no_of_errors;
}


        
        

        
            

